package com.scpii.universal.dispatch.tools;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;

import javax.annotation.Resource;

import org.apache.tools.ant.Project;
import org.apache.tools.ant.taskdefs.Zip;
import org.ini4j.InvalidFileFormatException;
import org.ini4j.Wini;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import com.scpii.universal.concurrent.TaskPool;
import com.scpii.universal.dispatch.Dispatcher;
import com.scpii.universal.dispatch.Const.DispathConfig;
import com.scpii.universal.dispatch.Const.IniConfig;
import com.scpii.universal.dispatch.domain.CompileTask;
import com.scpii.universal.dispatch.service.CompileTaskService;
import com.scpii.universal.pb.UniversalProtocol.BuildAppConfigs;
import com.scpii.universal.pb.UniversalProtocol.DownLoadPicInfo;
import com.scpii.universal.pb.UniversalProtocol.GetPackInfoSC;
import com.scpii.universal.pb.UniversalProtocol.GuideBarInfo;
import com.scpii.universal.pb.UniversalProtocol.IniCoreInfo;
import com.scpii.universal.pb.UniversalProtocol.PageInfo;
import com.scpii.universal.util.DownloadFileUtil;

/**
 * 打包工具类
 * 
 * @author chenxin
 */

@Component
public class PackTool {

	private static final Logger logger = LoggerFactory
			.getLogger(PackTool.class);

	@Resource
	private CompileTaskService compileTaskService;

	/**
	 * 生成加工临时目录:打包目录,ini文件生成路径,ini文件,图片资源目录,打包文件路径
	 * 
	 * @param packDir
	 *            打包目录
	 * @param iniFilePath
	 *            ini文件生成路径
	 * @param iniFile
	 *            ini文件
	 * @param picFileDir
	 *            图片资源目录
	 * @param packFilePath
	 *            打包文件路径
	 * @throws IOException
	 */
	private Map<String, Object> makeTempDir(String packDir, String iniFilePath,
			File iniFile, String picFileDir, String packFilePath,
			String configName) throws IOException {
		Map<String, Object> param = new HashMap<String, Object>();
		packDir = DispathConfig.UNIVERSAL_DISPATCH_TEMP_DIR + File.separator
				+ configName;
		param.put("packDir", packDir);
		iniFilePath = DispathConfig.UNIVERSAL_DISPATCH_TEMP_DIR
				+ File.separator + configName + File.separator
				+ DispathConfig.ZIP_SUB_CONFIG + File.separator
				+ DispathConfig.CONFIG_INI;
		param.put("iniFilePath", iniFilePath);
		iniFile = new File(iniFilePath);
		File parentFile = iniFile.getParentFile();
		if (!parentFile.exists()) {
			parentFile.mkdirs();
		}
		if (!iniFile.exists()) {
			iniFile.createNewFile();
		}
		File storeFile = new File(DispathConfig.UNIVERSAL_DISPATCH_STORE_DIR);
		if (!storeFile.exists()) {
			storeFile.mkdir();
		}
		param.put("iniFile", iniFile);
		picFileDir = DispathConfig.UNIVERSAL_DISPATCH_TEMP_DIR + File.separator
				+ configName + File.separator + DispathConfig.ZIP_SUB_RESOURCE
				+ File.separator;

		File picFile = new File(picFileDir);
		if (!picFile.exists()) {
			picFile.mkdirs();
		}
		param.put("picFileDir", picFileDir);
		packFilePath = DispathConfig.UNIVERSAL_DISPATCH_CONFIG_DIR
				+ File.separator + configName + IniConfig.ZIP_SUFIX;

		param.put("packFilePath", packFilePath);
		return param;
	}

	/**
	 * 生成ini配置文件
	 * 
	 * @param service
	 *            应用编译任务管理
	 * @param iniCoreInfo
	 *            配置核心信息
	 * @param guideBarInfo
	 *            导航bar信息
	 * @param iniFile
	 *            ini文件名
	 * @throws InvalidFileFormatException
	 * @throws IOException
	 */
	private void generateIni(CompileTaskService service,
			IniCoreInfo iniCoreInfo, GuideBarInfo guideBarInfo,
			List<BuildAppConfigs> buildAppConfigs, File iniFile, String pushType)
			throws InvalidFileFormatException, IOException {
		logger.info("生成ini文件开始");
		Wini ini = new Wini(iniFile);
		int appId = iniCoreInfo.getAppId();
		int enterpriseId = iniCoreInfo.getEnterpriseId();
		String serverHost = DispathConfig.SERVER_HOST_API;
		int templete = iniCoreInfo.getStyleId();
		String googleAnalytics = iniCoreInfo.getGoogleAnalytics() == null ? "UA-33716926-1"
				: iniCoreInfo.getGoogleAnalytics();
		if (googleAnalytics == null || "".equals(googleAnalytics.trim())) {
			googleAnalytics = "UA-33716926-1";
		}
		String AppUpdateVersion = String.valueOf(iniCoreInfo.getAppVersion());

		logger.info("生成ini文件:导航功能模块");
		ini.put(IniConfig.GUIDE_BAR, IniConfig.PAGE, guideBarInfo.getPageList());
		List<PageInfo> pageInfos = guideBarInfo.getPageInfosList();
		for (PageInfo pageInfo : pageInfos) {
			ini.put(IniConfig.GUIDE_BAR, pageInfo.getPageInfoName(),
					pageInfo.getPageInfoValue());
		}

		logger.info("生成ini文件:核心配制信息");
		ini.put(IniConfig.CORE_INFO, IniConfig.APP_ID, appId);
		ini.put(IniConfig.CORE_INFO, IniConfig.ENTERPRISE_ID, enterpriseId);
		ini.put(IniConfig.CORE_INFO, IniConfig.SERVER_HOST, serverHost);
		/**
		 * 修改内容：在ini文件中新增push接口地址 修改人：左柳波 修改时间：2012-12-20
		 */
		ini.put(IniConfig.CORE_INFO, IniConfig.SERVER_PUSH_API,
				DispathConfig.SERVER_PUSH_API);
		ini.put(IniConfig.CORE_INFO, IniConfig.API, DispathConfig.API);
		ini.put(IniConfig.CORE_INFO, IniConfig.APP_SECRET,
				DispathConfig.APP_SECRET);
		ini.put(IniConfig.CORE_INFO, IniConfig.TEMPLATE, templete);
		ini.put(IniConfig.CORE_INFO, IniConfig.NAVIGATE_STYLE_ID,
				guideBarInfo.getNavigateStyleId());
		ini.put(IniConfig.CORE_INFO, IniConfig.GOOGLE_ANALYTICS,
				googleAnalytics);
		ini.put(IniConfig.CORE_INFO, IniConfig.APP_UPDATE_VERSION,
				AppUpdateVersion);
		// 给出默认值
		Map<String, String> configMap = new HashMap<String, String>();
		configMap.put("REGISTER_ALLOW_KEY", DispathConfig.REGISTER_ALLOW_KEY);
		configMap.put("T_SINA_APP_KEY", DispathConfig.DEFAULT_SINA_APP_KEY);
		configMap.put("WX_APP_KEY", DispathConfig.DEFAULT_WX_APP_KEY);
		configMap.put("T_TENCENT_APP_KEY",
				DispathConfig.DEFAULT_TENCENT_APP_KEY);
		configMap.put("WX_APP_ID", DispathConfig.DEFAULT_WX_APP_ID);
		configMap.put("T_SINA_APP_ID", DispathConfig.DEFAULT_SINA_APP_ID);
		configMap.put("T_TENCENT_APP_ID", DispathConfig.DEFAULT_TENCENT_APP_ID);
		for (BuildAppConfigs config : buildAppConfigs) {
			configMap.put(config.getKey(), config.getValue());
		}
		for (Map.Entry<String, String> entry : configMap.entrySet()) {
			ini.put(IniConfig.CORE_INFO, entry.getKey(), entry.getValue());
		}
		String appVersionExtend = appId + ".T" + DispathConfig.ENVIRONMENT
				+ pushType;
		ini.put(IniConfig.CORE_INFO, "AppVersionExtend", appVersionExtend);
		ini.store();
		ini.store();
		logger.info("生成ini文件结束");
	}

	/**
	 * 下载图片资源
	 * 
	 * @param cd
	 *            同步工具类对象
	 * @param downPicTaskPool
	 *            文件下载线程池
	 * @param downLoadPicInfos
	 *            下载源
	 * @param picFileDir
	 *            下载图片信息
	 * @param osType
	 *            操作系统类型
	 * @throws NumberFormatException
	 * @throws InterruptedException
	 */
	private void downLoadPic(CountDownLatch cd, TaskPool<Void> downPicTaskPool,
			List<DownLoadPicInfo> downLoadPicInfos, String picFileDir,
			Integer osType) throws NumberFormatException, InterruptedException {
		logger.info("图片下载执行开始");
		final List<DownLoadPicInfo> downLoadPicInfosIn = downLoadPicInfos;
		int sourceCount = downLoadPicInfos.size();
		cd = new CountDownLatch(sourceCount);
		for (int i = 0; i < sourceCount; i++) {
			final int seq = i;
			final Integer osTypeIn = osType;
			final String picFileDirIn = picFileDir;
			final CountDownLatch cdIn = cd;
			downPicTaskPool.submitTask(new Callable<Void>() {
				@Override
				public Void call() throws Exception {
					try {
						DownLoadPicInfo downLoadPicInfo = downLoadPicInfosIn
								.get(seq);
						String fileName = downLoadPicInfo.getPicName();
						String url = downLoadPicInfo.getPicUrl();
						logger.info("任务下载图片资源[" + url + "]执行开始");

						if (url != null && !url.trim().equals("")) {
							if (osTypeIn == Integer
									.parseInt(DispathConfig.ANDROID_APP_OS_TYPE)) {
								new AndroidPicDownloadHandler().downloadPic(
										picFileDirIn, fileName, url);
							} else {
								new IosPicDownloadHandler().downloadPic(
										picFileDirIn, fileName, url);
							}
						}
						logger.info("任务下载图片资源执行完毕");
					} catch (Exception e) {
						e.getStackTrace();
						logger.info("任务下载图片资源执行异常，异常原因：" + e.getMessage());
					} finally {
						cdIn.countDown();
					}
					return null;
				}
			});
		}
		cd.await();
		logger.info("图片下载执行结束");
	}

	/**
	 * 下载IOS证书 2012-11-26 zuoliubo 新增
	 * 
	 * @param cd
	 *            计数器
	 * @param downCertificationTaskPool
	 *            下载证书线程池
	 * @param certificationList
	 *            证书的URL地址列表
	 * @param destinationFolder
	 *            证书存放的路径文件夹
	 * @throws InterruptedException
	 */
	private void downCertification(CountDownLatch cd,
			TaskPool<Void> downCertificationTaskPool,
			List<String> certificationList, String destinationFolder)
			throws InterruptedException {

		if (certificationList.size() == 0) {
			logger.info("IOS证书不存在");
			return;
		}
		logger.info("下载IOS证书开始");
		final String folder = destinationFolder; // 下载的目标文件夹
		int certificationSize = certificationList.size();
		cd = new CountDownLatch(certificationSize);
		for (int i = 0; i < certificationSize; i++) {

			final int seq = i;
			final CountDownLatch countDownLatch = cd;
			final List<String> list = certificationList;

			downCertificationTaskPool.submitTask(new Callable<Void>() {
				@Override
				public Void call() throws Exception {
					// 下载每一个证书
					try {
						DownloadFileUtil.downloadFile(list.get(seq), folder,
								null);
					} catch (Exception e) {
						logger.error("下载证书失败  URL:" + list.get(seq));
						logger.error(e.getMessage());
					} finally {
						countDownLatch.countDown();
					}
					return null;
				}
			});
		} // end of for
		cd.await();
		logger.info("下载IOS证书结束");
	}

	/**
	 * 生成打包文件
	 * 
	 * @param packDir
	 *            打包路径
	 * @param packFilePath
	 *            打包文件路径
	 * @throws IOException
	 * @throws InterruptedException
	 */
	private void doZip(String packDir, String packFilePath) {
		logger.info("开始执行压缩");
		Zip zip = new Zip();
		zip.setBasedir(new File(packDir));
		zip.setDestFile(new File(packFilePath));
		Project p = new Project();
		p.setBaseDir(new File(packDir));
		zip.setProject(p);
		zip.execute();
		logger.info("结束执行压缩");
	}

	/**
	 * 打包
	 * 
	 * @param task
	 *            打包任务
	 * @throws InvalidFileFormatException
	 * @throws IOException
	 * @throws NumberFormatException
	 * @throws InterruptedException
	 */
	public void pack(CompileTask task) throws InvalidFileFormatException,
			IOException, NumberFormatException, InterruptedException {

		String packDir = "";
		String iniFilePath = "";
		File iniFile = null;
		String picFileDir = "";
		String packFilePath = "";
		int downPicTaskPoolNum = Integer
				.parseInt(DispathConfig.DOWNLOAD_PIC_TASK_POOL_NUM);// 图片下载线程池处理线程数
		TaskPool<Void> downPicTaskPool = new TaskPool<Void>(downPicTaskPoolNum);// 图片下载线程池处理线程池
		int downCertificationTaskPoolNum = 2;// 证书下载线程池处理线程数
		TaskPool<Void> downCertificationTaskPool = new TaskPool<Void>(
				downCertificationTaskPoolNum);// 下载IOS证书的线程池
		CountDownLatch cd = null;
		CountDownLatch cdCertification = null;
		Integer taskId = task.getCompileTaskId();
		Integer osType = task.getAppOSType();
		Integer appUpdateId = task.getAppUpdateId();
		// 从web端调用接口获取数据
		GetPackInfoSC packInfo = compileTaskService.getPackInfo(appUpdateId);

		IniCoreInfo iniCoreInfo = packInfo.getIniCoreInfo();
		logger.info("iniCoreInfo:" + iniCoreInfo);
		List<BuildAppConfigs> buildAppConfigs = packInfo
				.getBuildAppConfigsList();
		int count = 0;
		CompileTask ctt = Dispatcher.tasks.get(taskId);
		for (BuildAppConfigs config : buildAppConfigs) {
			if ("WX_APP_ID".equals(config.getKey())) {
				ctt.setWxAppId(config.getValue());
				count++;
			}
			if ("WX_APP_KEY".equals(config.getKey())) {
				ctt.setWxAppKey(config.getValue());
				count++;
			}
		}

		if (count != 2) {
			ctt.setWxAppId(DispathConfig.DEFAULT_WX_APP_ID);
			ctt.setWxAppKey(DispathConfig.DEFAULT_WX_APP_KEY);
		}

		Dispatcher.tasks.put(taskId, ctt);

		List<String> certificationList = iniCoreInfo.getCertificationList();
		GuideBarInfo guideBarInfo = packInfo.getGuideBarInfo();
		List<DownLoadPicInfo> downLoadPicInfos = packInfo
				.getDownLoadPicInfosList();
		String configName = task.getConfigName();

		Map<String, Object> param = makeTempDir(packDir, iniFilePath, iniFile,
				picFileDir, packFilePath, configName);
		packDir = param.get("packDir").toString();
		iniFilePath = param.get("iniFilePath").toString();
		iniFile = (File) param.get("iniFile");
		picFileDir = param.get("picFileDir").toString();
		packFilePath = param.get("packFilePath").toString();

		String pushType = "X";
		// 如果是编译IOS，将下载ios有关的证书 即cer和mobileprovision文件
		if (osType == 1) {
			String certificationFileDir = DispathConfig.UNIVERSAL_DISPATCH_TEMP_DIR
					+ File.separator
					+ configName
					+ File.separator
					+ DispathConfig.ZIP_SUB_CERTIFICATION + File.separator;
			File cerFile = new File(certificationFileDir);
			if (cerFile.exists()) {
				cerFile.delete();
			}
			cerFile.mkdir();
			logger.info("certificationList:" + certificationList);
			if (certificationList.size() > 0) {
				String name = certificationList.get(0);
				name = name.substring(name.lastIndexOf("/") + 1);
				if ("gotoapp".equals(name.split("\\.")[0])) {
					pushType = "G";
				} else {
					pushType = "A";
				}
			}

			downCertification(cdCertification, downCertificationTaskPool,
					certificationList, certificationFileDir);
		}

		// 生成ini文件
		generateIni(compileTaskService, iniCoreInfo, guideBarInfo,
				buildAppConfigs, iniFile, pushType);
		// 下载图片资源
		downLoadPic(cd, downPicTaskPool, downLoadPicInfos, picFileDir, osType);
		//
		// 如果是编译IOS，将下载ios有关的证书 即cer和mobileprovision文件
		if (osType == 1) {
			String certificationFileDir = DispathConfig.UNIVERSAL_DISPATCH_TEMP_DIR
					+ File.separator
					+ configName
					+ File.separator
					+ DispathConfig.ZIP_SUB_CERTIFICATION + File.separator;
			File cerFile = new File(certificationFileDir);
			if (cerFile.exists()) {
				cerFile.delete();
			}
			cerFile.mkdir();

			logger.info("certificationList:" + certificationList);
			downCertification(cdCertification, downCertificationTaskPool,
					certificationList, certificationFileDir);
		}

		// 压缩文件
		doZip(packDir, packFilePath);
		compileTaskService.updateCompletePackTask(taskId);
	}

}